#include "fsreportpersistenceassistant.h"

#include "../documents/data/reportdata.h"
#include "../documents/swpreportdocument.h"
#include "fspersistencecommon.h"
#include "fsreportpersistentdocument.h"

#include <icontainerdocument.h>
#include <ilinkeddocument.h>
#include <istorage.h>
#include <persistence.h>
#include <serializationengine.h>

#include <QtCore/QDataStream>
#include <QtCore/QUrl>
#include <QtGui/QStandardItemModel>

using namespace core;
using namespace fspersistence;
using namespace swp;

typedef QSharedPointer<IDocument> Document;

FsReportPersistenceAssistant::FsReportPersistenceAssistant(
    const MimeType& mimeType) : IPersistenceAssistant(),
        m_mimeType(mimeType),
        m_persistentDocument(),
        m_document()
{

}

FsReportPersistenceAssistant::~FsReportPersistenceAssistant()
{

}

// IPersistenceAssistant
bool FsReportPersistenceAssistant::canOpen(
    const MimeType& mimeType) const
{
    return mimeType == m_mimeType;
}

bool FsReportPersistenceAssistant::canSave(
    const MimeType& mimeType) const
{
    return mimeType == m_mimeType;
}

bool FsReportPersistenceAssistant::isNative(
    const QSharedPointer<IPersistentDocument>& document) const
{
    return document->mimeType() == m_mimeType;
}

QString FsReportPersistenceAssistant::forStorage() const
{
    return FILE_SYSTEM_STORAGE;
}

bool FsReportPersistenceAssistant::isNative(
    const MimeType& mimeType) const
{
    return mimeType == m_mimeType;
}

QSharedPointer<IDocument> FsReportPersistenceAssistant::open()
{
    Q_ASSERT(!m_persistentDocument.isNull());

    const QSharedPointer<FsReportPersistentDocument> _document =
        qSharedPointerDynamicCast<FsReportPersistentDocument>(
            m_persistentDocument);

    Q_ASSERT(!_document.isNull());

    const IStorage* storage = _document->storage();
    const QByteArray data = storage->read(m_persistentDocument);
    _document->updateData(data);

    QDataStream stream(data);
    stream.setVersion(QDataStream::Qt_4_7);

    QString name;

    stream >> name;

    if (m_document.isNull())
    {
        m_document = QSharedPointer<IDocument>(new SwpReportDocument(
            name, _document->mimeType(),
            QSharedPointer<IContainerDocument>(0), true));
    }
    else
    {
        m_document->rename(name);
    }

    m_document->setFuture(false);

    ReportData* rData =  dynamic_cast<ReportData* >(m_document->data());

    Q_ASSERT(rData != 0);

    bool isValid = false;

    stream >> isValid;

    QString timeStamp;
    stream >> timeStamp;
    rData->setTimeStamp(timeStamp);

    QStandardItemModel* model = new QStandardItemModel();
    unserializeExecs(stream, model);
    rData->setExecutions(model);

    model = new QStandardItemModel();
    unserializeTable(stream, model);
    rData->setTokens(model);

    model = new QStandardItemModel();
    unserializeTable(stream, model);
    rData->setSteps(model);

    m_document->setValid(isValid);

    m_document->setDirty(false);

    return m_document;
}

QSharedPointer<IDocument> FsReportPersistenceAssistant::openFuture()
{
    Q_ASSERT(!m_persistentDocument.isNull());

    const QSharedPointer<FsReportPersistentDocument> _document =
        qSharedPointerDynamicCast<FsReportPersistentDocument>(
            m_persistentDocument);

    Q_ASSERT(!_document.isNull());

    const IStorage* storage = _document->storage();
    const QByteArray data = storage->read(m_persistentDocument);
    _document->updateData(data);

    QDataStream stream(data);
    stream.setVersion(QDataStream::Qt_4_7);

    QString name;

    stream >> name;

    m_document = QSharedPointer<IDocument>(new SwpReportDocument(name,
        _document->mimeType(),
        QSharedPointer<IContainerDocument>(0), true));

    m_document->setDirty(false);

    return m_document;
}

QSharedPointer<IPersistentDocument>
    FsReportPersistenceAssistant::save()
{
    Q_ASSERT(!m_document.isNull());

    QByteArray data;

    QDataStream stream(&data, QIODevice::WriteOnly);
    stream.setVersion(QDataStream::Qt_4_7);

    stream << m_document->name();

    stream << m_document->isValid();

    ReportData* repData = dynamic_cast<ReportData* >(m_document->data());

    stream << repData->timeStamp();

    const QStandardItemModel* model = repData->executions();
    serializeExecs(stream, model);

    model = repData->tokens();
    serializeTable(stream, model);

    model = repData->steps();
    serializeTable(stream, model);

    //if (serializedProperly && serializedProperly1 && serializedProperly2)
    //{
        m_persistentDocument->updateData(data);
        m_persistentDocument->storage()->write(m_persistentDocument);
    //}

    return m_persistentDocument;
}

void FsReportPersistenceAssistant::closeAndKeepFuture()
{
    Q_ASSERT(!m_document.isNull());

    ReportData* rData =  dynamic_cast<ReportData* >(m_document->data());

    Q_ASSERT(rData != 0);

    rData->clearData();

    m_document->setDirty(false);
    m_document->setFuture();
}

bool FsReportPersistenceAssistant::createPersistentDocument(
    const QUrl& url, const IStorage* storage)
{
    bool ret = false;

    QFileInfo info(url.toLocalFile());

    if (info.exists())
    {
        QSharedPointer<IPersistentDocument> document(
            new FsReportPersistentDocument(info, storage));

        m_persistentDocument = document;

        ret = true;
    }

    return ret;
}

QSharedPointer<IDocument>
    FsReportPersistenceAssistant::getDocument() const
{
    return m_document;
}

QSharedPointer<IPersistentDocument> FsReportPersistenceAssistant::
    getPersistentDocument() const
{
    return m_persistentDocument;
}

IPersistenceAssistant* FsReportPersistenceAssistant::copy() const
{
    FsReportPersistenceAssistant* ret =
        new FsReportPersistenceAssistant(m_mimeType);

    return ret;
}

void FsReportPersistenceAssistant::setDocument(
    const QSharedPointer<IDocument>& document)
{
    if (m_document.isNull())
    {
        m_document = document;
    }
    else
    {
        Q_ASSERT(false); // document is already set
    }
}

void FsReportPersistenceAssistant::setPersistentDocument(
    const QSharedPointer<IPersistentDocument>& document)
{
    if (m_persistentDocument.isNull())
    {
        m_persistentDocument = document;
    }
    else
    {
        Q_ASSERT(false); // document is already set
    }
}

QSharedPointer<IPersistenceAssistant>
    FsReportPersistenceAssistant::getNativeAssistant(
        const QSharedPointer<IDocument>& document) const
{
    QList<QSharedPointer<IPersistenceAssistant> > assistants =
        Persistence::getNativeAssistants(document);

    QSharedPointer<IPersistenceAssistant> assistant;

    foreach (const QSharedPointer<IPersistenceAssistant>& _assistant,
        assistants)
    {
        if (!_assistant->getPersistentDocument().isNull())
        {
            assistant = _assistant;
            break;
        }
        else
        {
            Persistence::closeAssistant(_assistant);
        }
    }

    return assistant;
}

void FsReportPersistenceAssistant::findAndAddLinks(
    const QStringList& linkUrls)
{
    const QSharedPointer<ILinkedDocument> linkedDocument =
        qSharedPointerDynamicCast<ILinkedDocument>(m_document);

    const QSharedPointer<IContainerDocument> container =
        m_document->container();

    const QStringList categories = container->categories();

    foreach (const QString& linkUrl, linkUrls)
    {
        const QUrl _linkUrl = QUrl::fromLocalFile(linkUrl);

        foreach (const QString& category, categories)
        {
            const QList<Document> documents = container->documents(category);

            foreach (const Document& document, documents)
            {
                const QSharedPointer<IPersistenceAssistant> assistant =
                    getNativeAssistant(document);

                if (!assistant.isNull() &&
                    !assistant->getPersistentDocument().isNull() &&
                    assistant->getPersistentDocument()->url() == _linkUrl)
                {
                    linkedDocument->addLink(document);
                    break;
                }
            }
        }
    }
}

void FsReportPersistenceAssistant::serializeTable(
    QDataStream& stream, const QStandardItemModel* model)
{
    stream << model->rowCount() << model->columnCount();

    for (int i = 0; i < model->rowCount(); ++i)
    {
        for (int j = 0; j < model->columnCount(); ++j)
        {
            QStandardItem* item = model->item(i, j);
            if (item != 0)
            {
                stream << item->text();
            }
            else
            {
                stream << "###"; // filler
            }
        }
    }
}

void FsReportPersistenceAssistant::serializeExecs(QDataStream& stream,
    const QStandardItemModel* model)
{
    stream << model->rowCount() << model->columnCount();

    for (int i = 0; i < model->rowCount(); ++i)
    {
        for (int j = 0; j < model->columnCount() - 1; ++j)
        {
            QStandardItem* item = model->item(i, j);
            if (item != 0)
            {
                stream << item->text();
            }
            else
            {
                stream << "###"; // filler
            }
        }

        QStandardItem* execs = model->item(i, model->columnCount() - 1);

        QString _execs = "";

        for (int j = 0; j < execs->columnCount(); ++j)
        {
            _execs += QString("%1|").arg(execs->child(0, j)->text());
        }

        stream << _execs;
    }
}

void FsReportPersistenceAssistant::unserializeTable(QDataStream& stream,
    QStandardItemModel* model)
{
    int rowCount = 0;
    int columnCount = 0;
    stream >> rowCount;
    stream >> columnCount;

    for (int i = 0; i < rowCount; ++i)
    {
        QList<QStandardItem* > row;
        for (int j = 0; j < columnCount; ++j)
        {
            QString text;
            stream >> text;

            if (text != "###")
            {
                QStandardItem* item = new QStandardItem(text);
                row.append(item);
            }
        }

        model->appendRow(row);
    }
}

void FsReportPersistenceAssistant::unserializeExecs(QDataStream& stream,
    QStandardItemModel* model)
{
    int rowCount = 0;
    int columnCount = 0;
    stream >> rowCount;
    stream >> columnCount;

    for (int i = 0; i < rowCount; ++i)
    {
        QList<QStandardItem* > row;
        for (int j = 0; j < columnCount - 1; ++j)
        {
            QString text;
            stream >> text;

            if (text != "###")
            {
                QStandardItem* item = new QStandardItem(text);
                row.append(item);
            }
        }

        QString execs;
        stream >> execs;

        QStringList _execs = execs.split("|", QString::SkipEmptyParts);

        QStandardItem* execsItem = new QStandardItem();

        QList<QStandardItem*> execsRow;

        foreach (const QString& exec, _execs)
        {
            execsRow.append(new QStandardItem(exec));
        }

        execsItem->appendRow(execsRow);

        row.append(execsItem);

        model->appendRow(row);
    }
}
